package affcom;

import java.awt.*;
import java.awt.image.*;

public class TNixie extends Canvas
{
    private static final int wchr =  11;  // Size of a Nixie character
    private static final int hchr =  19;

    private Color           tint;        // Color of the digits
    private int             ndig, ndec;  // Number of digits and decimals
    private int             wcou, hcou;  // whole counter
    private Dimension       size;         //Size counter
    private double          dval;        // Counter value & text
    private String          text;
    private Image[]         imag;        // Array of images


//-----------------------------------------------------

// Constructor, green, no decimals
// 1st argument is number of digits
	public TNixie(int ns)
        {
		this(Color.green, ns, 0);
	}

//-----------------------------------------------------

// Constructor, green characters
// 1st argument is number of digits
// 2nd argument is number of decimals
	public TNixie(int ns, int nd)
        {
		this(Color.green, ns, nd);
	}

//-----------------------------------------------------

// Constructor, no decimal places
// 1st argument is required color
// 2nd argument is number of digits
	public TNixie(Color co, int ns)
        {
		this(co, ns, 0);
	}

//-----------------------------------------------------

// Constructor
// 1st argument is required color
// 2nd argument is number of digits
// 3rd argument is number of decimals
	public TNixie(Color co, int ns, int nd)
        {
		ndig = Math.max(ns  ,        1);
		ndec = Math.min(nd  , ndig - 3);
		ndec = Math.max(ndec,        0);
		wcou = ndig *  (wchr + 2)  + 6 ;
		hcou =          hchr       + 8 ;
		size = new Dimension(wcou, hcou);
                setTint(co);
		setValu(0.);
	}

//-----------------------------------------------------
// From this point on, routines are in alphabetic order
//-----------------------------------------------------

// Increments the counter
	public void addValu()
        {
		setValu(dval + 1.);
	}

//-----------------------------------------------------

// Increments the counter
// 1st argument is an increment
	public void addValu(int iv)
        {
		setValu(dval + iv);
	}

//-----------------------------------------------------

// Increments the counter
// 1st argument is an increment
	public void addValu(double dv)
        {
		setValu(dval + dv);
	}

//-----------------------------------------------------

// Returns minimum size of canvas
	public Dimension getMinimumSize()
        {
		return size;
	}

//-----------------------------------------------------

// Returns preferred size of canvas
	public Dimension getPreferredSize()
        {
		return size;
	}

//-----------------------------------------------------

// Returns text in counter
	public String getText()
        {
                //System.out.println("text: " + text);
		return text;
	}

//-----------------------------------------------------

// Returns color of digits
	public Color getTint()
        {
		return tint;
	}

//-----------------------------------------------------

// Returns value in counter
	public double getValu()
        {
		return dval;
	}

//-----------------------------------------------------

// Paints centered counter
	public void paint(Graphics gr)
        {
		gr.setColor(getBackground());
		Dimension sz = getSize();
		int ix = (sz.width  - wcou) / 2;
		int iy = (sz.height - hcou) / 2;
		gr.draw3DRect(ix, iy, wcou - 1, hcou - 1, false);
		ix = ix + 1;
		iy = iy + 1;
		gr.draw3DRect(ix, iy, wcou - 3, hcou - 3, false);
		ix = ix + 1;
		iy = iy + 1;
		gr.setColor  (Color.black);
		gr.fillRect  (ix, iy, wcou - 4, hcou - 4);
		ix = ix + 2;
		iy = iy + 2;
		String vc = " +-.0123456789:Eor";
		for (int i = 0; i < ndig; i = i + 1) {
			int id = (int)text.charAt(i);
			id = vc.indexOf(id);
			if (id < 0) id = 0;
			gr.drawImage(imag[id], ix, iy, this);
			ix = ix + wchr + 2;
		}
	}

//-----------------------------------------------------

// Places an integer into a rectangular array
// 1st argument is the integer
// 2nd argument is x coordinate (column)
// 3rd argument is y coordinate ( row  )
// 4th argument is the array
	private void setadot(int k, int ix, int iy, byte[] px)
        {
		int i = iy * wchr + ix;
		int j = (int)px[i];
		if (k > j) px[i] = (byte)k;
	}

//-----------------------------------------------------

// Updates text directly
// 1st argument is the required text, which will be
// truncated when long, or right-filled with spaces.
	public void setText(String st)
        {
		String tx = st;
		int nc = tx.length();
		if (nc > ndig) {
			nc = ndig;
			tx = tx.substring(0, nc);
		}
		while (nc < ndig) {
			tx = tx + " ";
			nc = nc +  1 ;
		}
		try {
			dval = Double.valueOf(tx).doubleValue();
		}
		catch (Exception ex) {
			dval = 0;
		}
		text = tx;
		if (isShowing()) repaint();
	}

//-----------------------------------------------------

// Sets color of characters
// 1st argument is the required color
	public void setTint(Color co)
        {

// Nixie character formed from a combination of 18 lozenge shapes,
// numbered and shaped as follows;
//
//		  04  05
//		15  16  17						   .
//		12  13  14		  . . .			 . . .
//		  02  03		. . . . .	or	 . . .
//		09  10  11		  . . .			 . . .
//		06  07  08						   .
//		  00  01
//
// Positions of lozenge centers are given in "xl" and "yl", with
// (0,0) at top-left corner. Only characters in "vc" can be used,
// others changed to spaces. Character shapes defined in "pb",
// each "on" bit corresponding to a lozenge number that is lit.
// Unlit lozenges are displayed with half intensity.

		tint = co;
// X of Nixie lozenge centers
		int[] xl = {  3,  7,  3,  7,  3,  7
				   ,  1,  5,  9,  1,  5,  9
				   ,  1,  5,  9,  1,  5,  9};
// Y of Nixie lozenge centers
		int[] yl = { 17, 17,  9,  9,  1,  1
				   , 15, 15, 15, 11, 11, 11
				   ,  7,  7,  7,  3,  3,  3};
// Array of packed bits for characters
		int[] pb = {      0,   9228,     12,    128, 187251
				   ,  74880, 148095, 149823, 186636,  39231
				   ,  39807, 148664, 187263, 186687,   8320
				   ,  37503,   2895,    588};
// Build color model; 0-black, 1-dim, 2-bright
		int ir  = tint.getRed  ();
		int ig  = tint.getGreen();
		int ib  = tint.getBlue ();
		byte r[] = {0,  (byte)(ir / 2), (byte)ir};
		byte g[] = {0,  (byte)(ig / 2), (byte)ig};
		byte b[] = {0,  (byte)(ib / 2), (byte)ib};
		IndexColorModel cm = new
		IndexColorModel(2, 3, r, g, b);
// Loop thru set of valid characters
		int nb = wchr * hchr;
		int nc = pb.length;
		int nl = xl.length;
		int nh = nl / 3;
		imag = new Image[nc];
		MediaTracker mt = new MediaTracker(this);
		for (int i = 0; i < nc; i = i + 1) {
			ig = pb[i];
			byte[] px = new byte[nb];
			for (ir = 0; ir < nb; ir = ir + 1) px[ir]=0;
// Loop thru lozenges placing them in array
			for (ir = 0; ir < nl; ir = ir + 1) {
				ib = ig % 2;
				ig = ig / 2;
				ib = ib + 1;
				int ix = xl[ir];
				int iy = yl[ir];
				setadot(ib, ix - 1, iy - 1, px);
				setadot(ib, ix - 1, iy    , px);
				setadot(ib, ix - 1, iy + 1, px);
				setadot(ib, ix    , iy - 1, px);
				setadot(ib, ix    , iy    , px);
				setadot(ib, ix    , iy + 1, px);
				setadot(ib, ix + 1, iy - 1, px);
				setadot(ib, ix + 1, iy    , px);
				setadot(ib, ix + 1, iy + 1, px);
				if (ir < nh) {
					setadot(ib, ix - 2, iy, px);
					setadot(ib, ix + 2, iy, px);
				}
				else {
					setadot(ib, ix, iy - 2, px);
					setadot(ib, ix, iy + 2, px);
				}
			}
// Use array and color model to get image
			MemoryImageSource mi =
				new MemoryImageSource(
					wchr, hchr, cm, px, 0, wchr);
			imag[i] = createImage(mi);
			mt.addImage(imag[i], 0);
		}
		try { mt.waitForAll(); }
		catch (Exception ex) { }
		if (isShowing()) repaint();
	}

//-----------------------------------------------------

// Updates the counter
// 1st argument is the required value
	public void setValu(int iv) {
		setValu((double)iv);
	}

//-----------------------------------------------------

// Updates the counter, which will be left-filled
// with spaces, or replaced with "Error" if long.
// 1st argument is the required value
	public void setValu(double dv) {
		dval = dv;
		String tx = Long.toString(Math.round(
					Math.abs(    dval) *
					Math.pow(10, ndec)));
		int nc = tx.length();
		if (ndec > 0) {
			while (nc <= ndec) {
				tx = "0" + tx;
				nc = nc  +  1;
			}
			int nb = nc - ndec;
			tx = tx.substring(0, nb) + "." +
				 tx.substring(   nb);
			nc = nc  +  1;
		}
		if (dval < 0) {
			tx = "-" + tx;
			nc = nc  +  1;
		}
		if (nc > ndig) {
			tx = "Error";
			nc = Math.min(5, ndig);
			tx = tx.substring(0, nc);
		}
		while (nc < ndig) {
			tx = " " + tx;
			nc = nc  +  1;
		}
		text = tx;
		if (isShowing()) repaint();
	}

//-----------------------------------------------------

// Decrements the counter
	public void subValu() {
		setValu(dval - 1.);
	}

//-----------------------------------------------------

// Decrements the counter
// 1st argument is a decrement
	public void subValu(int iv) {
		setValu(dval - iv);
	}

//-----------------------------------------------------

// Decrements the counter
// 1st argument is a decrement
	public void subValu(double dv) {
		setValu(dval - dv);
	}

//-----------------------------------------------------

// Updates display without clearing, to avoid flicker
	public void update(Graphics gr) {
		paint(gr);
	}

//-----------------------------------------------------

}